package auth

import (
	"app/common"
	rich_err "app/error"
	"app/models"
	"app/service"
	"github.com/gin-gonic/gin"
)

func GetPermission(context *gin.Context) (*models.GoadminPermissions, *rich_err.RichError) {
	data, err := service.Get[models.GoadminPermissions](context)
	if err != nil {
		return nil, rich_err.NewError(rich_err.RequestInvalid, err)
	}
	return data, nil
}

func GetPermissions(context *gin.Context) ([]*models.GoadminPermissions, *models.Pager, *rich_err.RichError) {
	base := models.Orm.Model(&models.GoadminPermissions{}).
		Select("goadmin_permissions.*")
	results, pager, err := service.GetDetails[models.GoadminPermissions](context, base, models.GoadminPermissions{}.TableName(), map[string]string{})
	if err != nil {
		return nil, nil, rich_err.NewError(rich_err.RequestInvalid, err)
	}
	return results, pager, nil
}

type UserQueryParams struct {
	Name string `form:"name"`
	Slug string `form:"slug"`
	// 可以添加更多需要支持筛选的字段及其对应的form标签
}

type RoleQueryParams struct {
	Name string `form:"name"`
	Slug string `form:"slug"`
	// 可以添加更多需要支持筛选的字段及其对应的form标签
}

func GetRole(id int) (*models.GoadminRoles, *rich_err.RichError) {
	data, err := GetRoleWithPermissions(uint(id))
	if err != nil {
		return nil, rich_err.NewError(rich_err.RequestInvalid, err)
	}
	return data, nil
}

func GetRoles(c *gin.Context) ([]*models.GoadminRoles, *models.Pager, *rich_err.RichError) {
	var queryParams RoleQueryParams
	if err := c.ShouldBindQuery(&queryParams); err != nil {
		return nil, nil, rich_err.NewError(rich_err.RequestInvalid, err)
	}

	var roles []*models.GoadminRoles
	query := models.Orm.Table("goadmin_roles")

	if queryParams.Name != "" {
		query = query.Where("name LIKE ?", "%"+queryParams.Name+"%")
	}
	if queryParams.Slug != "" {
		query = query.Where("slug = ?", queryParams.Slug)
	} else {
		query = query.Where("slug IN (?)", common.ROLE_SLUGS)
	}
	// 根据需要添加更多筛选条件
	query, pager := service.BuildPage(c.Request.URL.Query(), query)
	if err := query.Find(&roles).Error; err != nil {
		return nil, nil, rich_err.NewError(rich_err.RequestInvalid, err)
	}

	for _, role := range roles {
		if err := fetchPermissionsForRole(role); err != nil {
			return nil, nil, rich_err.NewError(rich_err.RequestInvalid, err)
		}
	}

	return roles, pager, nil
}

func GetUser(id int) (*models.GoadminUsers, *rich_err.RichError) {
	data, err := GetUserWithRolesAndPermissions(uint(id))
	if err != nil {
		return nil, rich_err.NewError(rich_err.RequestInvalid, err)
	}
	return data, nil
}

func GetUsers(c *gin.Context) ([]*models.GoadminUsers, *models.Pager, *rich_err.RichError) {
	var queryParams UserQueryParams
	if err := c.ShouldBindQuery(&queryParams); err != nil {
		return nil, nil, rich_err.NewError(rich_err.RequestInvalid, err)
	}

	var users []*models.GoadminUsers
	query := models.Orm.Table("goadmin_users")

	if queryParams.Name != "" {
		query = query.Where("name LIKE ?", "%"+queryParams.Name+"%")
	}
	query = query.Joins("JOIN goadmin_role_users ON goadmin_role_users.user_id = goadmin_users.id").
		Joins("JOIN goadmin_roles ON goadmin_roles.id = goadmin_role_users.role_id")
	// 根据传入的slug筛选出含有指定slug的role的user
	if queryParams.Slug != "" {
		query = query.Where("goadmin_roles.slug = ?", queryParams.Slug)
	} else {
		// 过滤slug是指定值的user
		query = query.Where("goadmin_roles.slug IN (?)", common.ROLE_SLUGS)
	}

	query, pager := service.BuildPage(c.Request.URL.Query(), query)

	if err := query.Find(&users).Error; err != nil {
		return nil, nil, rich_err.NewError(rich_err.RequestInvalid, err)
	}

	for _, user := range users {
		if err := fetchRolesForUser(user); err != nil {
			return nil, nil, rich_err.NewError(rich_err.RequestInvalid, err)
		}
		if err := fetchPermissionsForUser(user); err != nil {
			return nil, nil, rich_err.NewError(rich_err.RequestInvalid, err)
		}
	}

	return users, pager, nil
}
func GetUserWithRolesAndPermissions(userID uint) (*models.GoadminUsers, error) {
	var user models.GoadminUsers
	if err := models.Orm.First(&user, userID).Error; err != nil {
		return nil, err
	}

	if err := fetchRolesForUser(&user); err != nil {
		return nil, err
	}
	if err := fetchPermissionsForUser(&user); err != nil {
		return nil, err
	}

	return &user, nil
}

func fetchRolesForUser(user *models.GoadminUsers) error {
	var roleUserLinks []struct {
		RoleID uint
	}
	if err := models.Orm.Table("goadmin_role_users").Where("user_id = ?", user.ID).Find(&roleUserLinks).Error; err != nil {
		return err
	}

	roleIDs := make([]uint, len(roleUserLinks))
	for i, link := range roleUserLinks {
		roleIDs[i] = link.RoleID
	}

	var roles []*models.GoadminRoles
	if err := models.Orm.Where("id IN (?)", roleIDs).Find(&roles).Error; err != nil {
		return err
	}

	user.Roles = roles
	return nil
}

func fetchPermissionsForUser(user *models.GoadminUsers) error {
	var permissionRoleLinks []struct {
		PermissionID uint
	}
	user.Permissions = make([]*models.GoadminPermissions, 0)
	for _, role := range user.Roles {
		if err := models.Orm.Table("goadmin_role_permissions").Where("role_id = ?", role.ID).Find(&permissionRoleLinks).Error; err != nil {
			return err
		}

		rolePermissionIDs := make([]uint, len(permissionRoleLinks))
		for i, link := range permissionRoleLinks {
			rolePermissionIDs[i] = link.PermissionID
		}

		var permissions []*models.GoadminPermissions
		if err := models.Orm.Where("id IN (?)", rolePermissionIDs).Find(&permissions).Error; err != nil {
			return err
		}

		role.Permissions = permissions
		user.Permissions = append(user.Permissions, role.Permissions...)
	}

	return nil
}

func GetRoleWithPermissions(roleID uint) (*models.GoadminRoles, error) {
	var role models.GoadminRoles
	if err := models.Orm.First(&role, roleID).Error; err != nil {
		return nil, err
	}

	if err := fetchPermissionsForRole(&role); err != nil {
		return nil, err
	}

	return &role, nil
}

func fetchPermissionsForRole(role *models.GoadminRoles) error {
	var permissionRoleLinks []struct {
		PermissionID uint
	}
	if err := models.Orm.Table("goadmin_role_permissions").Where("role_id = ?", role.ID).Find(&permissionRoleLinks).Error; err != nil {
		return err
	}

	permissionIDs := make([]uint, len(permissionRoleLinks))
	for i, link := range permissionRoleLinks {
		permissionIDs[i] = link.PermissionID
	}

	var permissions []*models.GoadminPermissions
	if err := models.Orm.Where("id IN (?)", permissionIDs).Find(&permissions).Error; err != nil {
		return err
	}

	role.Permissions = permissions
	return nil
}
